package com.example.spider;

import cn.hutool.core.io.FileUtil;
import org.shoulder.core.util.JsonUtils;

import java.io.File;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.stream.Collectors;

public class IpPortDistributeAnalyzer {

    private static final String projectPath = "D:\\code\\java\\self\\spider\\";
    private static final String testPath = projectPath + "src/test/java/com/example/spider/source/";

    public static void testDeleteWhenIterate() throws InterruptedException {
        ConcurrentHashMap<String, String> m = new ConcurrentHashMap<>();
        for (int i = 0; i < 10; i++) {
            m.put("" + i, "" + i);
        }
        Iterator<String> it = m.values().iterator();
        new Thread(() -> {
            while (it.hasNext()) {
                try {
                    String s = it.next();
                    System.out.println(s);
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }).start();
        Thread.sleep(2000);
        m.remove("1");
        m.remove("5");
        m.remove("7");
    }


    public static void main(String[] args) throws InterruptedException {
//        testDeleteWhenIterate();
//         将ip:port解析为port并统计各个port出现频次
//        List<String> ipPortList = FileUtil.readUtf8Lines("F:/spider/ipProxy/allIpPort.txt")
//                .stream().parallel().map(l -> {
//                    if (l.contains(":")) {
//                        String[] arr = l.split(":");
//                        boolean ok = arr.length == 2 && AddressUtils.isIpv4(arr[0]) && AddressUtils.isPort(arr[1]);
//                        return ok ? l : null;
//                    }
//                    return null;
//                })
//                .filter(Objects::nonNull)
//                .collect(Collectors.toList());
//        analyzeDistribution(ipPortList, IpPortDistributeAnalyzer::parseIp2Parts, List.of(4, 8, 16, 32, 64, 128, 256), testPath + "ip2PartDistribution.txt");
//        analyzeDistribution(ipPortList, IpPortDistributeAnalyzer::parsePort, List.of(4, 8, 16, 32, 64, 128), testPath + "portDistribution.txt");
        String jsonStr = FileUtil.readUtf8String(new File("F:/spider/ipProxy/ex.txt"));
        ConcurrentHashMap<String, AtomicInteger> keyCountMap = JsonUtils.parseObject(jsonStr, ConcurrentHashMap.class, String.class, AtomicInteger.class);
        analyzeDistribution(keyCountMap, List.of(4, 8, 16), testPath + "error.txt");
    }

    public static String parseIp2Parts(String ipAndPort) {
        String[] arr = ipAndPort.split("\\.");
        if (arr.length != 4) {
            System.out.println(ipAndPort);
        }
        return arr[0] + "." + arr[1];
    }

    public static String parsePort(String ipAndPort) {
        String[] arr = ipAndPort.split(":");
        return arr[1];
    }

    public static void analyzeDistribution(List<String> contextLines, Function<String, String> parseFunction, List<Integer> proportionSeed, String keyAndCountDisorderFile) {
        ConcurrentHashMap<String, AtomicInteger> keyCountMap = new ConcurrentHashMap<>();
        contextLines.parallelStream()
                .forEach(line ->
                        keyCountMap.computeIfAbsent(parseFunction.apply(line), k -> new AtomicInteger()).incrementAndGet()
                );
        analyzeDistribution(keyCountMap, proportionSeed, keyAndCountDisorderFile);
    }

    public static void analyzeDistribution(ConcurrentHashMap<String, AtomicInteger> keyCountMap, List<Integer> proportionSeed, String keyAndCountDisorderFile) {
        // map 无法根据key排序，这里用集合维护key排序
        int total = keyCountMap.values().stream().map(AtomicInteger::get).reduce(Integer::sum).orElse(0);
        List<KeyCount<String>> keyAndCountDisorderList = keyCountMap.entrySet().stream()
                .map(e -> new KeyCount<>(e.getKey(), e.getValue().get()))
                .sorted(Comparator.comparingInt(KeyCount::getCount))
                .collect(Collectors.toList());
        // 从频繁到不频繁排序
        Collections.reverse(keyAndCountDisorderList);
        // 分析不同情况下的分布与占比
        List<Summary> summaries = proportionSeed.stream()
                .map(t -> new Summary(t, total))
                .collect(Collectors.toList());
        int max = proportionSeed.stream().max(Integer::compareTo).orElseThrow();

        for (int i = 0; i < max; i++) {
            String key = keyAndCountDisorderList.get(i).k;
            for (Summary summary : summaries) {
                if (i < summary.top) {
                    summary.count.addAndGet(keyCountMap.get(key).get());
                }
            }
        }
        // output
        List<String> resultLines = keyAndCountDisorderList.stream().map(KeyCount::toString).collect(Collectors.toList());
        resultLines.addAll(0, summaries.stream().map(Summary::toString).collect(Collectors.toList()));
        FileUtil.writeUtf8Lines(resultLines, keyAndCountDisorderFile);

    }

    public static class Summary {
        final int top;
        final AtomicInteger count;
        final int total;

        public Summary(int top, int total) {
            this.top = top;
            this.total = total;
            this.count = new AtomicInteger();
        }

        public String percentage() {
            return String.format("%.2f", count.get() * 100.0 / total);
        }

        public String toString() {
            return "TOP " + top + " percentage=" + percentage() + ", total=" + total;
        }

    }

    public static class KeyCount<KEY extends Comparable<KEY>> {
        KEY k;
        int count;

        public KeyCount(KEY k, int count) {
            this.k = k;
            this.count = count;
        }

        /**
         * Getter method for property <tt>k</tt>.
         *
         * @return property value of k
         */
        public KEY getK() {
            return k;
        }

        /**
         * Getter method for property <tt>count</tt>.
         *
         * @return property value of count
         */
        public int getCount() {
            return count;
        }

        @Override
        public String toString() {
            return k + "\t-----\t" + count;
        }
    }

}
